home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Libraries / Sherlock 2.0 / DevLibSrc / Main_DevLib / LIBmem.c < prev    next >
Text File  |  1996-03-16  |  16KB  |  573 lines

  1. /*
  2.     devlib: Memory management routines and node allocation routines.
  3.  
  4.     source:  LIBmem.c
  5.     started: September 20, 1985
  6.     version: January 7, 1994.
  7.         November 15, 1995.
  8.             Added mem_free_life_node.
  9.         November 7, 1995.
  10.             Replace sprintf with cvt_l2s.
  11.             Call end_abort instead of abort.
  12.         July 13, 1994.
  13.             Bug fix to mem_new_block (!)
  14. */
  15.  
  16. #include <LIBlib.h>
  17. #include <LIBend.h>
  18. #include <LIBlist.h>
  19. #include <LIBmem.h>
  20. #include <LIBobj.h>
  21.  
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25.  
  26. /*
  27.     Define global variables.
  28. */
  29. mem_life * mem_life_list = NULL;
  30.  
  31. /*
  32.     Define local variables.
  33. */
  34. static long mem_max_node_tag = 12;    /* strlen("big blocks:")+1 */
  35. static long mem_max_byte_tag = 11;    /* strlen("big bytes:")+1 */
  36.  
  37. /*
  38.     Compute the maximum statistics for the given lifetime.
  39. */
  40. void
  41. mem_compute_max_stats (register mem_life * life)
  42. {
  43.     FTAG("mem_compute_max_stats");
  44.     register mem_stat * stat_p = NULL;
  45.     STATB(ftag);
  46.     
  47.     /* Update the global statistics. */
  48.     life -> mem_max_blocks        = max(life -> mem_max_blocks,    life -> mem_cur_blocks);
  49.     life -> mem_max_nodes        = max(life -> mem_max_nodes,    life -> mem_cur_nodes);
  50.     life -> mem_max_bytes        = max(life -> mem_max_bytes,    life -> mem_cur_bytes);
  51.     life -> mem_max_waste        = max(life -> mem_max_waste,    life -> mem_cur_waste);
  52.     life -> mem_max_big_blocks    = max(life -> mem_max_big_blocks,    life -> mem_cur_big_blocks);
  53.     life -> mem_max_big_bytes    = max(life -> mem_max_big_bytes,    life -> mem_cur_big_bytes);
  54.     
  55.     /* Update the maxima statistics in all attached statistics nodes. */
  56.     for (stat_p = life -> mem_life_stat_list; stat_p; stat_p = stat_p -> next) {
  57.         stat_p -> mem_max_stat_nodes = max(stat_p -> mem_max_stat_nodes, stat_p -> mem_cur_stat_nodes);
  58.         stat_p -> mem_max_stat_bytes = max(stat_p -> mem_max_stat_bytes, stat_p -> mem_cur_stat_bytes);
  59.     }
  60.     
  61.     STATX(ftag);
  62. }
  63.  
  64. /*
  65.     Print memory statistics.
  66.     Unlike object statistics, these are available in the production program.
  67. */
  68. #define NODES_TAG "cur nodes"
  69. #define BYTES_TAG "cur bytes"
  70.  
  71. #define MAX_NODES_TAG "max nodes"
  72. #define MAX_BYTES_TAG "max bytes"
  73.  
  74. #define TOT_NODES_TAG "tot nodes"
  75. #define TOT_BYTES_TAG "tot bytes"
  76.  
  77. #ifndef PRODUCTION /* Define this routine only if there is a Sherlock window. */
  78.  
  79. void
  80. mem_dump_stats(register mem_life * life)
  81. {
  82.     FTAG("mem_dump_stats");
  83.     register mem_stat * stat_p = NULL;
  84.     register mem_life * mlp = NULL;
  85.  
  86.     long tag_width = mem_max_node_tag;
  87.     
  88.     long bytes_width = strlen(NODES_TAG);
  89.     long nodes_width = strlen(BYTES_TAG);
  90.     
  91.     long max_bytes_width = strlen(NODES_TAG);
  92.     long max_nodes_width = strlen(BYTES_TAG);
  93.     
  94.     long tot_bytes_width = strlen(NODES_TAG);
  95.     long tot_nodes_width = strlen(BYTES_TAG);
  96.     
  97.     long tot_max_bytes = 0;
  98.     long tot_max_nodes = 0;
  99.     
  100.     long tot_cur_bytes = 0;
  101.     long tot_cur_nodes = 0;
  102.     
  103.     long blocks_width = 1;
  104.     long waste_width = 1;
  105.     long avail_width = 1;
  106.  
  107.     char buf [CVT_BUF_SIZE];
  108.     STATB(ftag);
  109.     
  110.     /* Recalculate the maximum statistics. */
  111.     mem_compute_max_stats(life);
  112.     
  113.     /*
  114.         Update the totals.
  115.         The current statistics will be cleared after we print them.
  116.     */
  117.     life -> mem_tot_blocks        += life -> mem_cur_blocks;
  118.     life -> mem_tot_nodes        += life -> mem_cur_nodes;
  119.     life -> mem_tot_bytes        +=life ->  mem_cur_bytes;
  120.     life -> mem_tot_waste        += life -> mem_cur_waste;
  121.     life -> mem_tot_big_blocks    += life -> mem_cur_big_blocks;
  122.     life -> mem_tot_big_bytes    += life -> mem_cur_big_bytes;
  123.  
  124.     /* Compute the sizes of maximum column widths in *all* lifetimes. */
  125.     for (mlp = mem_life_list; mlp; mlp = mlp -> mem_life_list) {
  126.         
  127.         for (stat_p = mlp -> mem_life_stat_list; stat_p; stat_p = stat_p -> next) {
  128.             tag_width = max(tag_width, strlen(stat_p -> mem_stat_node_tag));
  129.         }
  130.         
  131.         #if 0 /* Avoid sprintf so we don't pull in the whole floating library. */
  132.  
  133.             bytes_width     = max(bytes_width,  sprintf(buf, "%ld", mlp -> mem_tot_bytes));
  134.             nodes_width     = max(nodes_width,  sprintf(buf, "%ld", mlp -> mem_tot_nodes));
  135.             blocks_width = max(blocks_width, sprintf(buf, "%ld", mlp -> mem_tot_blocks));
  136.             waste_width  = max(waste_width,  sprintf(buf, "%ld", mlp -> mem_tot_waste));
  137.             avail_width  = max(avail_width,  sprintf(buf, "%ld", mlp -> mem_last_avail));
  138.             
  139.         #else
  140.         
  141.             bytes_width     = max(bytes_width,  cvt_l2s(buf, mlp -> mem_tot_bytes));
  142.             nodes_width     = max(nodes_width,  cvt_l2s(buf, mlp -> mem_tot_nodes));
  143.             blocks_width = max(blocks_width, cvt_l2s(buf, mlp -> mem_tot_blocks));
  144.             waste_width  = max(waste_width,  cvt_l2s(buf, mlp -> mem_tot_waste));
  145.             avail_width  = max(avail_width,  cvt_l2s(buf, mlp -> mem_last_avail));
  146.  
  147.         #endif
  148.     }
  149.     
  150.     max_bytes_width = max(max_bytes_width, bytes_width);
  151.     max_nodes_width = max(max_nodes_width, nodes_width);
  152.     tot_bytes_width = max(tot_bytes_width, bytes_width);
  153.     tot_nodes_width = max(tot_nodes_width, nodes_width);
  154.  
  155.     /*
  156.         Print the headings for the statistics.
  157.  
  158.         The 2's in these pad widths compensate for the calls to ecs() or es(": ").
  159.         These calls insure that each column starts with at least one blank.
  160.     */
  161.     ecnl();
  162.     es("Totals for "); es(life -> mem_life_name); es("...\n");
  163.     epads(" ",                tag_width);
  164.     epads(TOT_NODES_TAG,    tot_nodes_width+2);
  165.     epads(TOT_BYTES_TAG,    tot_bytes_width+2);
  166.     epads(MAX_NODES_TAG,    max_nodes_width+2);
  167.     epads(MAX_BYTES_TAG,    max_bytes_width+2);
  168.     epads(NODES_TAG,         nodes_width+2);
  169.     epads(BYTES_TAG,        bytes_width+2);
  170.     enl();
  171.  
  172.     /* Print the statistics for all attached statistics nodes. */
  173.     for (stat_p = life -> mem_life_stat_list; stat_p; stat_p = stat_p -> next) {
  174.     
  175.         /* Update the totals. */
  176.         stat_p -> mem_tot_stat_nodes += stat_p -> mem_cur_stat_nodes;
  177.         stat_p -> mem_tot_stat_bytes += stat_p -> mem_cur_stat_bytes;
  178.     
  179.         /* print the statistics. */
  180.         {
  181.             /* Print the tag field. */
  182.             ecnl();
  183.             epads(stat_p -> mem_stat_node_tag, tag_width); es(": ");
  184.             
  185.             /* Print the tot statistics. */
  186.             epadlong(stat_p -> mem_tot_stat_nodes, tot_nodes_width); ecs();
  187.             epadlong(stat_p -> mem_tot_stat_bytes, tot_bytes_width); ecs();
  188.             
  189.             /* Print the max statistics. */
  190.             epadlong(stat_p -> mem_max_stat_nodes, max_nodes_width); ecs();
  191.             epadlong(stat_p -> mem_max_stat_bytes, max_bytes_width); ecs();
  192.             
  193.             tot_max_nodes += stat_p -> mem_max_stat_nodes;
  194.             tot_max_bytes += stat_p -> mem_max_stat_bytes;
  195.             
  196.             /* Print the current statistics. */
  197.             epadlong(stat_p -> mem_cur_stat_nodes, nodes_width); ecs();
  198.             epadlong(stat_p -> mem_cur_stat_bytes, bytes_width);
  199.             
  200.             tot_cur_nodes += stat_p -> mem_cur_stat_nodes;
  201.             tot_cur_bytes += stat_p -> mem_cur_stat_bytes;            
  202.             enl();
  203.         }
  204.         
  205.         /* Reset the current stats so they won't be counted twice! */
  206.         stat_p -> mem_cur_stat_nodes = 0;
  207.         stat_p -> mem_cur_stat_bytes = 0;
  208.     }
  209.     
  210.     /* Print the totals lines. */
  211.     {
  212.         /* Print the grand totals. */
  213.         ecnl();
  214.         epads("TOTALS", tag_width); es(": ");
  215.         epadlong(life -> mem_tot_nodes,  tot_nodes_width); ecs();
  216.         epadlong(life -> mem_tot_bytes,  tot_bytes_width); ecs();
  217.         
  218.         epadlong(tot_max_nodes, max_nodes_width); ecs();
  219.         epadlong(tot_max_bytes, max_bytes_width); ecs();
  220.     
  221.         epadlong(tot_cur_nodes, nodes_width); ecs();
  222.         epadlong(tot_cur_bytes, bytes_width); enl();
  223.         
  224.         /* Print the maxima. */
  225.         ecnl();
  226.         epads("MAXIMA", tag_width); es(": ");
  227.         epadlong(life -> mem_max_nodes,  nodes_width); ecs();
  228.         epadlong(life -> mem_max_bytes,  bytes_width);
  229.         enl();
  230.         
  231.         /* Print the block statistics. */
  232.         
  233.         /* Always leave room for "N/A" */
  234.         avail_width = max(3, avail_width);
  235.         
  236.         es("tot blocks: ");    epadlong(life -> mem_tot_blocks, blocks_width); ecs();
  237.         es("tot waste: ");    epadlong(life -> mem_tot_waste,  waste_width);  ecs();
  238.         es("tot avail: ");    epadlong(life -> mem_last_avail, avail_width);    ecs();
  239.         epads("big blocks: ", 2); elong(life -> mem_tot_big_blocks); ecs();
  240.         es("big bytes: ");  elong(life -> mem_tot_big_bytes);
  241.         enl();
  242.         
  243.         es("max blocks: ");    epadlong(life -> mem_max_blocks, blocks_width); ecs();
  244.         es("max waste: ");    epadlong(life -> mem_max_waste,  waste_width);  ecs();
  245.         es("max avail: ");    epads("N/A", avail_width); ecs();
  246.         epads("big blocks: ", 2); elong(life -> mem_max_big_blocks); ecs();
  247.         es("big bytes: ");  elong(life -> mem_max_big_bytes);
  248.         ecnl();
  249.     }
  250.     
  251.     /* Reset the current stats so they won't be counted twice later. */
  252.     life -> mem_cur_blocks = 0;
  253.     life -> mem_cur_nodes = 0;
  254.     life -> mem_cur_bytes = 0;
  255.     life -> mem_cur_waste = 0;
  256.     life -> mem_cur_big_blocks = 0;
  257.     life -> mem_cur_big_bytes = 0;
  258.  
  259.     STATX(ftag);
  260. }
  261.  
  262. #endif /* n PRODUCTION */
  263.  
  264. /*
  265.     Free all blocks of the indicated lifetime and update statistics.
  266.     
  267.     Do _not_ free the lifetime itself or remove the lifetime from the list of lifetimes.
  268. */
  269. void
  270. mem_free_life(register mem_life * life)
  271. {
  272.     FTAG("mem_free_life");
  273.     register mem_stat * stat_p = NULL;
  274.     STATB(ftag);
  275.     ASSERT(life);
  276.  
  277.     /* Update the maximum statistic in all attached statistics nodes. */
  278.     for (stat_p = life -> mem_life_stat_list; stat_p; stat_p = stat_p -> next) {
  279.         
  280.         /* Update the maxima statistics.*/
  281.         stat_p -> mem_max_stat_nodes = max(stat_p -> mem_max_stat_nodes, stat_p -> mem_cur_stat_nodes);
  282.         stat_p -> mem_max_stat_bytes = max(stat_p -> mem_max_stat_bytes, stat_p -> mem_cur_stat_bytes);
  283.         
  284.         /* Update the total statistics. */
  285.         stat_p -> mem_tot_stat_nodes += stat_p -> mem_cur_stat_nodes;
  286.         stat_p -> mem_tot_stat_bytes += stat_p -> mem_cur_stat_bytes;
  287.         
  288.         /* Reset the current statistics. */
  289.         stat_p -> mem_cur_stat_nodes = 0;
  290.         stat_p -> mem_cur_stat_bytes = 0;
  291.     }
  292.     
  293.     /* Save the available byte in the last block. */
  294.     life -> mem_last_avail = life -> mem_avail;
  295.     
  296.     /* Recalculate the maximum statistics. */
  297.     mem_compute_max_stats(life);
  298.     
  299.     /* Update the totals. */
  300.     life -> mem_tot_blocks        += life -> mem_cur_blocks;
  301.     life -> mem_tot_nodes        += life -> mem_cur_nodes;
  302.     life -> mem_tot_bytes        +=life ->  mem_cur_bytes;
  303.     life -> mem_tot_waste        += life -> mem_cur_waste;
  304.     life -> mem_tot_big_blocks    += life -> mem_cur_big_blocks;
  305.     life -> mem_tot_big_bytes    += life -> mem_cur_big_bytes;
  306.  
  307.     life -> mem_cur_blocks = 0;
  308.     life -> mem_cur_nodes = 0;
  309.     life -> mem_cur_bytes = 0;
  310.     life -> mem_cur_waste = 0;
  311.     life -> mem_cur_big_blocks = 0;
  312.     life -> mem_cur_big_bytes = 0;
  313.     
  314.     /* Actually free the memory! */
  315.     lst_free_all_macro(life -> mem_block_list, mem_block);
  316.     life -> mem_block_list = NULL;
  317.     life -> mem_ptr = NULL;
  318.     life -> mem_avail = 0;
  319.     
  320.     STATX(ftag);
  321. }
  322.  
  323. /*
  324.     Free the lifetime node itself and remove it from the list of lifetimes.
  325. */
  326. void
  327. mem_free_life_node(register mem_life * life)
  328. {
  329.     FTAG("mem_free_life_node");
  330.     STATB(ftag);
  331.     PERM_ASSERT(life);
  332.     
  333.     /* Remove the life from the list of lifetimes. */
  334.     if (mem_life_list == life)
  335.     {
  336.         mem_life_list = life -> mem_life_list;
  337.     }
  338.     else
  339.     {
  340.         mem_life * p1 = mem_life_list;
  341.         mem_life * p2 = p1 -> mem_life_list;
  342.         
  343.         while(p2 && life != p2)
  344.         {
  345.             PERM_ASSERT(p2);
  346.             p1 = p2;
  347.             p2 = p1 -> mem_life_list;
  348.         }
  349.         p1 -> mem_life_list = p2 -> mem_life_list;
  350.     }
  351.  
  352.     /*
  353.         `mem_init_life' uses `lib_calloc' to allocate the life
  354.         because it assumes nothing has been initialzed.
  355.     */
  356.     lib_free(life);
  357.     
  358.     STATX(ftag);
  359. }
  360.  
  361. /*
  362.     Deallocate a statistic node.
  363. */
  364. void
  365. mem_free_stat_node(mem_stat * the_stat)
  366. {
  367.     PERM_ASSERT(the_stat);
  368.     lib_free(the_stat);
  369. }
  370.  
  371. /*
  372.     Allocate and initialize a life node.
  373.     
  374.     Assume nothing has been initialized,
  375.     so don't use Sherlock macros or obj_new_macro.
  376. */
  377. mem_life *
  378. mem_init_life(char * life_name)
  379. {
  380.     FTAG("mem_init_life");
  381.  
  382.     mem_life * life = NULL;
  383.     register mem_life * prev = NULL;
  384.     register mem_life * lp = NULL;
  385.     
  386.     life = lib_calloc((size_t) 1, sizeof(mem_life));
  387.     if (life == NULL) {
  388.         end_abort();
  389.     }
  390.     life -> mem_life_name = life_name;
  391.     
  392.     /* Add the life to the lifetime list in alphabetical  order. */
  393.     for (prev = NULL, lp = mem_life_list; lp; prev = lp, lp = lp -> mem_life_list) {
  394.         int n;
  395.         
  396.         n = strcmp(life_name, lp -> mem_life_name);
  397.         if (n == 0) {
  398.             err_fatal2("mem_init_life: duplicate life: ", life_name);
  399.         }
  400.         if (n < 0) {
  401.             break;
  402.         }
  403.     }
  404.  
  405.     /* Link the type descriptor into the alphabetical list. */
  406.     if (prev == NULL) {
  407.  
  408.         /* Link the life node before the first node. */
  409.         life -> mem_life_list = mem_life_list;
  410.         mem_life_list = life;
  411.     }
  412.     else {
  413.     
  414.         /* Link the life node after the prev node. */
  415.         ASSERT(mem_life_list);
  416.         life -> mem_life_list = prev -> mem_life_list;
  417.         prev -> mem_life_list = life;
  418.     }
  419.  
  420.     return life;
  421. }
  422.  
  423. /*
  424.     Initialize a new statistics node and
  425.     attach it to the statistics list for the indicated lifetime.
  426.     Assume nothing has been initialized.
  427. */
  428. mem_stat *
  429. mem_init_stats(register mem_life * life, char * node_tag)
  430. {
  431.     FTAG("mem_init_stats");
  432.  
  433.     register mem_stat * prev = NULL;
  434.     register mem_stat * sp = NULL;
  435.     mem_stat * stat_p = NULL;
  436.     
  437.     if (life == NULL) {
  438.         end_abort();
  439.     }
  440.  
  441.     /* Create the statistic node. */
  442.     stat_p = lib_calloc(1, sizeof(mem_stat));
  443.     stat_p -> mem_stat_node_tag = node_tag;
  444.     
  445.     /* Update the maximum width of the tag column. */
  446.     mem_max_node_tag = max(mem_max_node_tag, strlen(node_tag)+1);
  447.  
  448.     /* Add the life to the lifetime list in alphabetical  order. */
  449.     for (
  450.         prev = NULL, sp = life -> mem_life_stat_list;
  451.         sp;
  452.         prev = sp, sp = sp -> next
  453.     ) {
  454.         int n;
  455.         
  456.         n = strcmp(node_tag, sp -> mem_stat_node_tag);
  457.         if (n == 0) {
  458.             err_fatal2("duplicate life statistic: ", node_tag);
  459.         }
  460.         if (n < 0) {
  461.             break;
  462.         }
  463.     }
  464.  
  465.     /* Link the stat_p into the alphabetical list. */
  466.     if (prev == NULL) {
  467.  
  468.         /* Link the stat_p node before the first node. */
  469.         stat_p -> next = life -> mem_life_stat_list;
  470.         life -> mem_life_stat_list = stat_p;
  471.     }
  472.     else {
  473.     
  474.         /* Link the stat_p node after the prev node. */
  475.         ASSERT(life -> mem_life_stat_list);
  476.         stat_p -> next = prev -> next;
  477.         prev -> next = stat_p;
  478.     }
  479.     
  480.     return stat_p;
  481. }
  482.  
  483. /*
  484.     Return a pointer to size bytes of memory allocated in some permanent block.
  485. */
  486. void *
  487. mem_new_big_block(size_t size, register mem_life * life, char * dtag)
  488. {
  489.     FTAG("mem_new_big_block");
  490.     mem_block * mbp = NULL;
  491.     void * result = NULL;
  492.     STATB(ftag);
  493.     ASSERT(dtag && life && life -> mem_avail >= 0 && size > 0);
  494.  
  495.     /* Update block statistics. */
  496.     MEM_STATS(
  497.         life -> mem_cur_big_blocks ++;
  498.         life -> mem_cur_big_bytes += size;
  499.     );
  500.     
  501.     /*
  502.         Allocate a big block. Do not change the status variables.
  503.         We can't use obj_new_macro here because dtag changes.
  504.     */
  505.     obj_new_var_tag_macro(mbp, size + sizeof(mem_block *), dtag);
  506.     
  507.     /* Add the block to the list. */
  508.     mbp -> next = life -> mem_block_list;
  509.     life -> mem_block_list = mbp;
  510.     
  511.     /* Point result at the first byte. */
  512.     result = &(mbp -> mem_data[0]);
  513.  
  514.     TRACEN("-mem_watch", sl_watch(mbp, size + sizeof(mem_block *), dtag));
  515.     
  516.     TRACEPX(ftag,
  517.         es(life -> mem_life_name); eblank(); elong(size); eblank();
  518.         eret(); eptr(result); enl());
  519.     return result;
  520. }
  521.  
  522. /*
  523.     Return a pointer to size bytes of memory allocated in a new block.
  524.     A new block is always allocated even if size < life -> mem_avail.
  525.     In fact, size may be zero, which allocates an unused new block.
  526. */
  527. void *
  528. mem_new_block(register size_t size, register mem_life * life)
  529. {
  530.     FTAG("mem_new_block");
  531.     mem_block * mbp = NULL;
  532.     void * result = NULL;
  533.     STATB(ftag);
  534.     ASSERT(life -> mem_avail >= 0 && size >= 0);
  535.  
  536.     /* Bug fix: 7/13/94: use MEM_ALLOC_SIZE, *not* MEM_BLOCK_SIZE here! */
  537.     if (size >= MEM_ALLOC_SIZE) {
  538.         result = mem_new_big_block(size, life, life -> mem_life_name);
  539.     }
  540.     else {
  541.  
  542.         /* Update block statistics. */
  543.         MEM_STATS(
  544.             life -> mem_cur_blocks++;
  545.             life -> mem_cur_waste += life -> mem_avail;
  546.         );
  547.  
  548.         /*
  549.             Get a standard sized permanent block.
  550.             We can't use obj_new_macro here because the tag can change.
  551.         */
  552.         obj_new_var_tag_macro(mbp, MEM_BLOCK_SIZE, life -> mem_life_name);
  553.  
  554.         /* Add the block to the list. */
  555.         mbp -> next = life -> mem_block_list;
  556.         life -> mem_block_list = mbp;
  557.  
  558.         /* Point result at the first byte. */
  559.         result = &(mbp -> mem_data[0]);
  560.  
  561.         /* Reserve space in the block. */
  562.         life -> mem_ptr = ((char *) result) + size;
  563.         life -> mem_avail = MEM_ALLOC_SIZE - size;
  564.  
  565.         TRACEN("-mem_watch", sl_watch(mbp, MEM_BLOCK_SIZE, "mem block"));
  566.     }
  567.  
  568.     TRACEPX(ftag,
  569.         es(life -> mem_life_name); eblank(); elong(size); eblank();
  570.         eret(); eptr(result); enl());
  571.     return result;
  572. }
  573.